// ------------------------------------------------------------------------------------
// REVISION LOG ENTRY
//
//    File name  : tkit2.js
// Revision By: (C) Copyright 2014 Hiroyuki Tominaga, All Rights Reserved.
//
//    Revised on : 2014-06-11 18:01:43
//    Summary    : CDN
//       https://raw.githubusercontent.com/motrohi/js-projects/master/js-tkit/tkit2.js
//       https://js-dragwindow.googlecode.com/svn/trunk/tkit2.js
//
// ------------------------------------------------------------------------------------
/**
 * class Tkit base.
 */
(function(name_space)
{
    var tkit, W; {
        W = (window || self);
        W.$ = W.$ || {};
        /* */
        tkit = W[name_space];
        (!tkit) && ( tkit = W[name_space] = {} );
        if (!W.$.noop) {
            W.$.noop = function(){};
        }
    }

    // undefined is not a "undefined"
    // typeof is returns "string" value.
    // ----------------------------------- oop functions. --------------------------------
    /* need for oop extendOverride */
    var _EXTENDED_ = $.generateId(6);

    function extendOverride(_this_clazz, _super) {
        var _this_prototype = _this_clazz.prototype;
        // overwrite _super argment
        _super = _super.prototype;

        for (var p in _super) {
            if(!_this_prototype[p]) // has not property?
                _this_prototype[p] = _super[p];
        }
        return _this_clazz;
    }
    //
    // verify Inheritance.
    //
    //
    function verifyInheritance(
        this_clazz, super_clazz
    ) {
        if (this_clazz === null)
            console.trace();
        if( !this_clazz[_EXTENDED_] ) {
            extendOverride(
                this_clazz, super_clazz
            )[_EXTENDED_] = true;
        }
    };

    /*
    [2010-11-25 11:17:32]
        継承するクラスは必ずこの関数を呼んで低レベルの初期化を行います。
        Initialize the class that inherits from the low level of this function must be called.
    */
    //
    // @param instance        :current instance of object. MUST this
    // @param this_clazz    :
    // @param super_clazz    :
    // @param args            :must be arguments object.
    //
    tkit.initAtom = function initAtom(
        instance,
        this_clazz, super_clazz,
        args
    ) {
        //acutual_ns.AfxObject.call(this, this_name);
        if(typeof(super_clazz) === "function")
            super_clazz.apply(instance, args);

        verifyInheritance(
            this_clazz, super_clazz
        );
    }
    // ----------------------------------- oop functions. --------------------------------
    /*
        void XMLHttpRequest.open(
            in AUTF8String method,
            in AUTF8String url,
            [optional] in boolean async,
            [optional] in AString user,
            [optional] in AString password
        );
    */
    tkit.loadAjax = function (src, callback, async/* optional */) {
        var _xhr = new XMLHttpRequest();
        _xhr.onreadystatechange = function() {
            if (this.readyState == XMLHttpRequest.DONE || this.status == 200) {
                if (typeof callback === "function") {
                    callback(this.responseText, this);
                }
            }
        };
        (async === undefined) && (async = false);
        _xhr.open('GET', src, async);
        _xhr.send();
    }

    // ----------------------------------- dynamic load. -----------------------------------
    var tkit_code_base = "http://jpropedit-gui.sourceforge.jp/tkitloader.php?file=";
    // helper
    function _eval_script(resonseText, xhr) {
        (window || self)["eval"](resonseText);
    }

    // delete するため, var keyword をつけない.
    _loaded_instance = undefined,     /* created class place holder. */
    _class_queue = undefined,         /* pending class queue(array */
    _bind_classes_callback = undefined, /* bindClasses callback function. */
    _pending_classes = 0;               /* pending class count */

    //var script_base = "https://js-dragwindow.googlecode.com/svn/trunk/",
    //var script_base = "https://raw.githubusercontent.com/motrohi/js-projects/master/js-tkit/",
    var script_base = "https://cdn.rawgit.com/motrohi/js-projects/master/js-tkit/",
    load_tkit_class = function(className) {
        _pending_classes++;
        var script = createE('script');
        script.onload = script.onerror = script.onreadystatechange = function() {
            if (
                script && ( !script.readyState || /loaded|complete/.test(script.readyState) )
            ) {
                // Handle memory leak in IE
                script.onerror = script.onload = script.onreadystatechange = null;

                checkPendingClasses();
                // Remove the script
                if (script.parentNode) {
                    script.parentNode.removeChild(script);
                }
                script = null;
            }
        };
        script.type = 'text/javascript';

        var head = $tags('head')[0];
		head.insertBefore(script, head.firstChild);
        /*var s = $tags('script')[0];
        s.parentElement.insertBefore(script, s.nextElementSibling);*/
        // tkit-dev.js から head tag に埋め込むと, $.genarateId(5) が $(=jQuery が認識されない...
        script.src = script_base + "tkit-" + className.toLowerCase() + ".js?id=" + new Date().getTime();
        // note: 2014-06-08 16:21:34
        //    IE10 では parentNode, nextSilbling を使うと失敗する.
        //s.parentNode.insertBefore(script, s.nextSilbling);
    };

    function checkPendingClasses() { // $.isFunction(_bind_classes_callback)
        if ( --_pending_classes <= 0 && _class_queue.length > 0 ) {
            tkit.DEBUG && console.log("load done, _pending_classes=%d", _pending_classes);
            // [fqdn, element, config]
            _class_queue.forEach(function(e, i, arry) {
                var tkit_class = new tkit[e[0]](e[1]);
                _bindClassesHelper(_loaded_instance, tkit_class, e[2]);
            });
            // Class load of all completed.
            _bind_classes_callback(_loaded_instance);

			delete _class_queue;
			delete _pending_classes;
			delete _bind_classes_callback;
			delete _loaded_instance;
        }
    }

    tkit.forName = function (className) {
        if (tkit[className] === undefined) {
            load_tkit_class(className);
            return null;
        }
        return tkit[className];
    }
    tkit.exists = function (className) {
        return tkit.forName(className) !== undefined;
    }
    /**
     * @param className the class name of create.
     * @param element   element id or element instance.
     */
    tkit.create = function (className) {
        // Object.create は匿名 class を作る.
        // よって, object instanceof Object しか成立しない.
        // しかし, constructor に作成元の function object を設定してやると
        // object instanceof ctor が成立するようになる.
        // この case で, Object.create を使うことの意義は,
        // function.apply を call することによる可変長 arguments の適用だろう.
        // new ctor とすると, arguments の展開が出来ない.
        var ctor = tkit.forName( Array.prototype.shift.call(arguments) );
        if (ctor === null) {
            return null;
        }
        var object = Object.create(ctor.prototype);
        ctor.apply(object, arguments);
        object.constructor = ctor
        return object;
    }
    // ----------------------------------- dynamic load class. --------------------------------

    // -------------------------------------------------------------------------------
    //
    //
    // -------------------------------------------------------------------------------
    tkit._bind_listener = $.noop;
    /**
     * bindClasses 実行時に call される.
     */
    tkit.setBindEventListener = function setBindEventListener(l) {
        tkit._bind_listener = (typeof l === "function")? l: $.noop;
    };

    /**
     * この function 完了後, setBindEventListener で set された listener は削除される.
     * @param cfg_id json を記述した element id.(innerText property を使用する
     *               または, data-tkit-classes に class name + ":" + config_id とすることで,
     *               個別に指定可能.( data-tkit-classes="DragWindow:dragwindow-cfg"
     *  json format:
     *  ------------------------------------------------------------
     *  {
     *      "<bind element id>": {
     *          <config for bind class>
     *      }
     *  }
     *  @param callback class が構築されるたびに instance が渡される.(必要ないときは function 以外の値を設定
     *                  function callback(tkit_class) {}
     *  @param var_name この値を指定すると, window[var_name] に load された class 参照が element id として bind される.
     *
     */
    tkit.bindClasses = function bindClasses(cfg_id, callback, var_name)
    {
        var tkit_elements = $("[data-tkit-class]");
        if (typeof callback !== "function")
            callback = tkit._bind_listener;

        // classes is place holder. json is json object
        var classes = {}, json; { // cfg_id を指定しない場合, 空の json object となる.
            json = document.getElementById(cfg_id);
            json = (json === null)? {}: JSON.parse(json.textContent);
            //classes.json_obj = json;
        }
        // cache call back, for invoke later.
        _bind_classes_callback = callback,
        // scope ないで global な var _loaded_instance に参照を入れておく.
        _loaded_instance = classes, _class_queue = []; {
			if (var_name !== undefined) {
				if (/(?:\-)(\w{1})/g.test(var_name)) {
					var_name = var_name.replace(/(?:\-)(\w{1})/g, function($0, $1) {
						//console.log("$0=%s, $1=%s", $0, $1);
						return $1.toUpperCase();
					});
				}
			}
			// assign global var name.
			W[var_name] = _loaded_instance;
        }

        tkit_elements.each(
            function(i, e) {
                var fqdn = e.getAttribute("data-tkit-class").split(":");
                var config  = (fqdn.length === 2)? fqdn[1]: json;
                fqdn = fqdn[0];
                try {
                    var tkit_class = tkit.create(fqdn, e); // class の load が必要な場合, 非同期で schedule される.
                    if (tkit_class) { // load されていない場合 null
                        _bindClassesHelper(classes, tkit_class, config);
                    } else {
                        _class_queue.push([fqdn, e, config]);
                        tkit.DEBUG && console.log("_class_queue=%o", _class_queue);
                    }
                } catch (e) {
                    console.log(e);
                }
            }
        );

        delete tkit._bind_listener;

        if (_class_queue.length === 0) {
            callback(classes);
            delete _class_queue;
            delete _pending_classes;
            delete _loaded_instance;
            delete _bind_classes_callback;
            return classes;
        }
        //return classes;
        //console.log("remove listener", delete tkit._bind_listener); // 2014-06-13 10:03:43 true, ok.
    };

    function _bindClassesHelper(classes, tkit_class, config) {
        classes[tkit_class._e.id] = tkit_class;
        tkit.DEBUG && console.log("_bindClassesHelper, class=%o", tkit_class);
        tkit_class.setOptions(config);
    }

    /**
     * base class である XWidget を直接継承する場合,
     * この shortcut function を利用可能.
     *
     * @param instance   MUST be "this".
     * @param this_clazz MUST be "this" constructor.
     * @param args       MUST be "arguments"
     *
     *    Tkit.extendBase(this, arguments);
     *
     * @see Tkit#initAtom(instance, this_clazz, super_clazz, arguments)
     */
    // note: 2014-06-25 15:56:49
    // 元は, extendBase(instance, args) だったが, instance.constructor を
    // this_clazz として使用するとうまくいかなかった...
    tkit.extendBase = function extendBase(instance, this_clazz, args) {
        var super_clazz = tkit.XWidget;
        super_clazz.apply(instance, args);
        verifyInheritance( this_clazz, super_clazz );
    }
    /**
     * class Tkit.XWidget
     *
     * Tkit package base class.
     */
    tkit.XWidget = function XWidget(element) {
        //this.attach(element); // これは error となった.
        this._e = typeof element === "string"? $dom(element): element;
    };
    tkit.XWidget.qname = name_space + ".XWidget";
    tkit.XWidget.prototype = {
        // to jQuery object.
        $: function () { return $(this._e); },
        attach: function (element) {
            this._e = typeof element === "string"? $dom(element): element;
            return this;
        },
        release: function () {
            // If needed, override...
            return this;
        },
        // 2014-07-01 03:27:59
        id: function () {
        	return this._e.id;
        },
        // 簡易 check function.
        is: function(element) {
            var e = this._e;
            if (!e) return false;
            return e.id === element || e === element;
        },

        install: $.noop,
        updateOptions: $.noop,
        /*
        * html element に plain text として設定情報を記述する.
        * 構文に関しては, class の実装ごとに異なる.
        */
        setOptions: function setOptions(config) {
            if (typeof config === "string") { // means config element id
                var e = $dom(config);
                if (e !== null) {
                    var src = e.getAttribute("data-src");
                    if (src !== null && src.length > 0) {
                        var _self = this;
                        function load_done(text) {
                            _self.updateOptions( JSON.parse(text) );
                        };
                        tkit.loadAjax(src, load_done);
                    }
                    else
                        this.updateOptions( JSON.parse(e.innerText) );

                    return;
                } else { // json string?
                    try {
                        config = JSON.parse(config);
                    } catch (e) {
                        console.log(e);
                    }
                }
            }
             // json object.
            if ($.isPlainObject(config))
                this.updateOptions(config);
        }
    };


    // -------------------------------------------------------------------------------
    tkit.getRandomColor = function getRandomColor(rgb)
    {
        var c;
        if(rgb) {
            c = "rgb(" +
                Math.floor(Math.random() * 256) + ", " +
                Math.floor(Math.random() * 256) + ", " +
                Math.floor(Math.random() * 256) + ")";
        }
        else {
            c = "#";
            for(var i = 0; i < 3; i++) {
                var hex = Math.floor(Math.random() * 256).toString(16);
                c += hex.length === 2? hex: "0" + hex;
            }
        }
        return c;
    };

})("Tkit");    // (function()

